package cn.edu.ysu.zry.entity;

import cn.edu.ysu.zry.util.ByteArray;
import cn.edu.ysu.zry.util.SHA256;

/**
 * @program: XieyiBlockChainOS
 * @description: 区块链的区块体对象
 * @author: Ruiyuan Zhang
 * @create: 2020-04-20 12:35
 */
public class BlockBody {
    // 协议列表的长度
    public static final int XIEYILIST_LENGTH=8;
    // merkle树的数量
    public static final int MERKLE_TREE_SIZE=15;
    // Merkle树的hash值
    public byte hashMerkle[]=new byte[SHA256.HASH_LENGTH];
    // xieyiList列表，由[Xieyi]对象组成的
    public Xieyi xieyiList[] = new Xieyi[XIEYILIST_LENGTH];
    // merkle树
    public byte merkleTree[][]=new byte[MERKLE_TREE_SIZE][SHA256.HASH_LENGTH];

    /**
    *@Description: 更新merkle树
    *@Param:
    *@return: merkle树的根hash值
    *@Author: Ruiyuan Zhang
    *@date: 2020/4/20
    */
    public byte[] updateMerkleTree() {
        // 0 如果没有任何协议内容，无须更新，返回空值
        if (xieyiList[0] == null) return null;
        // 1 将协议列表的内容填充到Merkle树中，最后的位置使用最前一个协议填充
        int startIndex = MERKLE_TREE_SIZE - XIEYILIST_LENGTH;
        for (int i = startIndex; i < MERKLE_TREE_SIZE; i++) {
            if (xieyiList[i - startIndex] == null) merkleTree[i] = xieyiList[0].hashXieyiAll;
            else merkleTree[i] = xieyiList[i - startIndex].hashXieyiAll;
        }
        // 2 计算根hash值
        byte root[] = calMerkleTree(0);
        return root;
    }
    /**
    *@Description: 计算merkle树x位置子树的root值
    *@Param: x 开始计算的二叉树中的位置
    *@return: merkle树x位置子树的root值
    *@Author: Ruiyuan Zhang
    *@date: 2020/4/20
    */
    public byte[] calMerkleTree(int x){
        // 0 递归边界
        int startIndex=MERKLE_TREE_SIZE-XIEYILIST_LENGTH;
        if(x>=startIndex)return xieyiList[x-startIndex].hashXieyiAll;
        // 1 获得左子树的值
        byte[]left=calMerkleTree(x*2+1);
        // 2 获得右子树的值
        byte[]right=calMerkleTree(x*2+2);
        // 3 计算本节点的值
        byte[]current=SHA256.getSHA256(ByteArray.arrayJoin(left,right));
        merkleTree[x]=current;
        // 4 返还本节点的值
        return current;
    }
    /**
    *@Description: 向协议列表中添加一条协议，并更新merkle树
    *@Param: xieyi 要添加的协议对象
    *@return: 更新后的根hash值，null时认为该区块已满
    *@Author: Ruiyuan Zhang
    *@date: 2020/4/20
    */
    public byte[] addXieyi(Xieyi xieyi){
        // 1 寻找要插入的位置，对于已满的直接返回false
        int insertIndex=-1;
        for (int i=0;i<xieyiList.length;i++){
            if(xieyiList[i]==null)insertIndex=i;
        }
        if(insertIndex==-1)return null;
        // 2 在指定位置上插入xieyi对象，并执行更新
        xieyiList[insertIndex]=xieyi;
        byte root[]=updateMerkleTree();
        return root;
    }
    /**
    *@Description: 验证协议是否存在在这个区块中
    *@Param: xieyi 要进行验证的协议
    *@return: 返回一个hash路径，null代表不存在
    *@Author: Ruiyuan Zhang
    *@date: 2020/4/20
    */
    public byte[][] verifyXieyi(Xieyi xieyi){
        // 0 对于空对象，不进行判断
        if(xieyi==null)return null;
        // 1 找到协议的位置，若没有找到就返回空值
        int index=-1;
        for(int i=0;i<xieyiList.length;i++){
            if(xieyiList[i]!=null&&xieyi.equals(xieyiList[i]))index=i;
        }
        if (index==-1) return null;
        int startIndex=MERKLE_TREE_SIZE-XIEYILIST_LENGTH;
        index+=startIndex;

        // 2 寻找路径，寻找策略：找到一条直到根节点的路径
        byte path[][]=new byte[(int)(Math.log(XIEYILIST_LENGTH)/Math.log(2))][SHA256.HASH_LENGTH];
        int insertIndex=0;
        while (index!=0){
            path[insertIndex++]=xieyiList[index^1].hashXieyiAll;
            index<<=1;
        }
        return path;
    }
    /**
    *@Description: 获取merkle树根hash值
    *@Param:
    *@return: merkle树根hash值
    *@Author: Ruiyuan Zhang
    *@date: 2020/4/20
    */
    public byte[] getMerkleTreeRootHash(){
        return calMerkleTree(0);
    }

    /**
     * getter and setter
     */
    public static int getXieyilistLength() {
        return XIEYILIST_LENGTH;
    }

    public static int getMerkleTreeSize() {
        return MERKLE_TREE_SIZE;
    }

    public byte[] getHashMerkle() {
        return hashMerkle;
    }

    public void setHashMerkle(byte[] hashMerkle) {
        this.hashMerkle = hashMerkle;
    }

    public Xieyi[] getXieyiList() {
        return xieyiList;
    }

    public void setXieyiList(Xieyi[] xieyiList) {
        this.xieyiList = xieyiList;
    }

    public byte[][] getMerkleTree() {
        return merkleTree;
    }

    public void setMerkleTree(byte[][] merkleTree) {
        this.merkleTree = merkleTree;
    }
}
